package org.lundberg.podcasts;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;

import org.lundberg.podcasts.R;
import org.lundberg.podcasts.types.Item;
import org.lundberg.podcasts.util.PodcastDB;
import org.lundberg.podcasts.util.Util;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.AsyncTask.Status;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;

public class DownloadService extends Service
{
	public static final String DOWNLOAD = "org.lundberg.podcasts.DOWNLOAD";
	public static final String START_DOWNLOAD = "org.lundberg.podcasts.START_DOWNLOAD";
	public static final String DOWNLOADING = "org.lundberg.podcasts.DOWNLOADING";
	public static final String FINISHED_DOWNLOAD = "org.lundberg.podcasts.FINISHED_DOWNLOAD";
	public static final String CANCEL = "org.lundberg.podcasts.CANCEL";
	private final IBinder mBinder = new LocalBinder();
	private DownloadTask downloadTask;
	

	@Override
	public IBinder onBind(Intent arg0) {
		return mBinder;
	}
	
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId)
	{		
		if (intent != null)
		{
			String action = intent.getAction();
			
			if (DOWNLOAD.equals(action))
			{
				ArrayList<String> guids = intent.getStringArrayListExtra("guid");
				
				// Send start downloading
				if (guids != null)
				{
					Intent startIntent = new Intent(DownloadService.START_DOWNLOAD);
					
					for (String guid : guids)
					{
						startIntent.putExtra("guid", guid);
						sendBroadcast(startIntent);
					}
						
				}
				
				if (downloadTask == null || Status.RUNNING.equals(downloadTask.status) == false) {
					
					downloadTask = new DownloadTask(guids);
					downloadTask.start();
				}
				else {
					
					downloadTask.addItems(guids);
				}
			}
			else if (CANCEL.equals(action)) {
				
				if (downloadTask != null) {
					
					downloadTask.abort(intent.getStringExtra("guid"));
				}
			}
		}
		
		return START_NOT_STICKY;
	}
	
	
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
		Log.d("DownloadService", "onDestroy");
	}
	
	
	
	public boolean isDownloading(String guid) {
		return guid != null && downloadTask != null && (guid.equals(downloadTask.currentItem) || downloadTask.queue.contains(guid));
	}

	
	
	/*
	public class DownloadTask extends AsyncTask<Void, Integer, Void>
	{
		private static final int NOTIFICATION_ID = 2;
		private static final int BUFFER_SIZE = 2048;
		private LinkedList<String> queue = new LinkedList<String>();
		private boolean abort = false;
		private NotificationManager mNotificationManager;
		private Notification notification;
		private String currentItem;
		private Intent updateIntent = new Intent(DownloadService.DOWNLOADING);
		
		public DownloadTask(ArrayList<String> items) {
			
			queue.addAll(items);
		}
		
		public void addItems(ArrayList<String> items)
		{	
			for (String item : items) {
				if (queue.contains(item) == false) {
					queue.add(item);
				}
			}
		}
		
		public void abort(String item)
		{	
			if (item == null) {
				return;
			}
			
			if (item.equals(currentItem)) {
			
				abort = true;
			}
			else {
			
				queue.remove(item);
				
				Intent finishedIntent = new Intent(DownloadService.FINISHED_DOWNLOAD);
	        	finishedIntent.putExtra("guid", item);
	        	sendBroadcast(finishedIntent);
			}
		}

		@Override
		public void onPreExecute()
		{
			mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
						
			notification = new Notification(R.drawable.download, null, System.currentTimeMillis());
			notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
			Intent mainIntent = new Intent(getApplicationContext(), MainActivity.class);
	        notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, mainIntent, 0);
	        notification.contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);
	        notification.contentView.setTextViewText(R.id.notification_title, "Downloading Podcast");
		}
		
		@Override
		protected Void doInBackground(Void... arg0)
		{
			PodcastDB helper = new PodcastDB(getApplicationContext());
			SQLiteDatabase db = helper.getReadableDatabase();
			
			while (queue.isEmpty() == false)
			{
				currentItem = queue.remove();
		    	
		    	Item item = null;
				
				Cursor c = db.query(false, PodcastDB.ITEMS_TABLE, new String[] {"guid", "channel_id", "title", "enclosureURL", "size"},
		                "guid='"+currentItem+"'", null, null, null, null, null);
		    	
		    	if (c.moveToFirst())
		    	{
		    		item = new Item(c);
		    		
		    		Log.d("DownloadService", "onStartCommand - added item to queue: "+item.title);
		    	}
		    	
		    	c.close();
				
				
				Log.d("DownloadTask", "run: "+item.guid);
    			
	    		String filename = Util.getPodcastFilename(item.guid);
	        	File localFile = new File(filename);
	        	
	        	if (localFile.exists() && localFile.length() == item.size) {
	        		Log.d("DownloadTask", "Already downloaded "+item.title);
	        		continue;
	        	}
	        	
	        	
	        	// Update the notification for this item
	        	notification.contentView.setTextViewText(R.id.notification_title, "Downloading "+PodcastDB.getChannelTitle(getApplicationContext(), item.channel_id));
	        	notification.contentView.setTextViewText(R.id.notification_item, item.title);
		        notification.contentView.setProgressBar(R.id.notification_progressbar, 100, 0, false);
		        mNotificationManager.notify(NOTIFICATION_ID, notification);
	        	
		        
	        	File tmpFile = new File(filename+".tmp");
	        	
	        	try
	            {
	                URL channelURL = new URL(item.enclosureURL);
	                HttpURLConnection conn = (HttpURLConnection) channelURL.openConnection();
	                conn.setRequestProperty("Range", "bytes="+(tmpFile.length())+"-");
	                conn.setConnectTimeout(5000);
	                conn.setReadTimeout(5000);
	                conn.connect();
	                
	                float length = conn.getContentLength();
	                
	                if (length <= 0) {
	                	length = 1.0f;
	                }
	                
	                BufferedInputStream in = new BufferedInputStream(conn.getInputStream(), BUFFER_SIZE);
	                FileOutputStream out = new FileOutputStream(tmpFile, true);
	                byte[] data = new byte[BUFFER_SIZE];
	                int bytes_read = (int) tmpFile.length();
	                int last_progress = 0;
	               	int r = 0;
	                int progress = 0;
	                
	                while (!abort && (r = in.read(data, 0, BUFFER_SIZE)) > -1)
	                {
	                	out.write(data, 0, r);
	                    bytes_read += r;
	                    
	                    // publish progress
	                    progress = (int) (bytes_read/length * 100.0f);
	                    
	                    if (progress > (last_progress + 1))
	                    {
	                    	publishProgress(progress);
	                    	last_progress = progress;
	                    }
	                }
	
	                in.close();
	                out.close();
	                conn.disconnect();
	                
	                if (!abort) {
	                	
	                	PodcastDB.setItemSize(getApplicationContext(), item.guid, (int)tmpFile.length());
	                	tmpFile.renameTo(localFile);
	                }
	            }
	            catch (SocketTimeoutException e) {
	        		Toast.makeText(getApplicationContext(), "Download of \""+item.title+"\" timed out", Toast.LENGTH_SHORT).show();
	        	}
	            catch (IOException e) {
	                e.printStackTrace();
	            }
	        	
	        	
	        	Intent finishedIntent = new Intent(DownloadService.FINISHED_DOWNLOAD);
	        	finishedIntent.putExtra("guid", item.guid);
	        	sendBroadcast(finishedIntent);
	        	
	        	publishProgress(100);
	        	
	        	// re-set the download flag
	            abort = false;
			}

			helper.close();

			currentItem = null;
			
			return null;
		}
		
		@Override
		public void onProgressUpdate(Integer...progress)
		{
			// Update the notification progress
			notification.contentView.setProgressBar(R.id.notification_progressbar, 100, progress[0], false);
	        mNotificationManager.notify(NOTIFICATION_ID, notification);
	        
	        updateIntent.putExtra("guid", currentItem);
	        updateIntent.putExtra("progress", progress[0]);
	        getApplicationContext().sendBroadcast(updateIntent);
		}
		
		@Override
		public void onPostExecute(Void result)
		{
			// cancel the notificatin for this Item
            mNotificationManager.cancel(NOTIFICATION_ID);
            
            mNotificationManager = null;
            updateIntent = null;
            notification = null;
            queue.clear();
		}
		
	}
	*/
	
	
	/**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class LocalBinder extends Binder {
        public DownloadService getService() {
            return DownloadService.this;
        }
    }
	
	
	
	private class DownloadTask extends Thread
	{
		private LinkedList<String> queue = new LinkedList<String>();
		private boolean abort;
		private String currentItem;
		private Status status;
		
		
		public DownloadTask(ArrayList<String> items) {
			
			queue.addAll(items);
			status = Status.RUNNING;
			abort = false;
		}
		
		public void addItems(ArrayList<String> items)
		{	
			for (String item : items) {
				if (queue.contains(item) == false) {
					queue.add(item);
				}
			}
		}
		
		public void abort(String item)
		{	
			if (item == null) {
				return;
			}
			
			if (item.equals(currentItem)) {
			
				abort = true;
			}
			else {
			
				queue.remove(item);
				
				Intent finishedIntent = new Intent(DownloadService.FINISHED_DOWNLOAD);
	        	finishedIntent.putExtra("guid", item);
	        	sendBroadcast(finishedIntent);
			}
		}

		@SuppressWarnings("deprecation")
		@SuppressLint("NewApi")
		@Override
		public void run()
		{
			PodcastDB helper = new PodcastDB(getApplicationContext());
			SQLiteDatabase db = helper.getReadableDatabase();
			final int BUFFER_SIZE = 2048;
			final int NOTIFICATION_ID = 2;
			byte[] data = new byte[BUFFER_SIZE];
			int bytes_read = 0;
            int progress = 0, last_progress = 0;
           	int r = 0;
            float length = 0.0f;
			
            // Set up the notification
            NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            
            Notification notification = null;
            
            try
            {
	            notification = new Notification.Builder(getApplicationContext())
		        .setSmallIcon(android.R.drawable.stat_sys_download)
		        .setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0,
		        											new Intent(getApplicationContext(), MainActivity.class), 0))
		        .setContent(new RemoteViews(getPackageName(), R.layout.custom_notification_layout))
		        .build();
            }
            catch (NoSuchMethodError e) {
            	
            	notification = new Notification(android.R.drawable.stat_sys_download, null, System.currentTimeMillis());
    			notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
    			Intent mainIntent = new Intent(getApplicationContext(), MainActivity.class);
    	        notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, mainIntent, 0);
    	        notification.contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);
            }
	        
	        notification.contentView.setTextViewText(R.id.notification_title, "Downloading Podcast");
	        
			while (queue.isEmpty() == false)
			{
				currentItem = queue.remove();
		    	Item item = null;
				Cursor c = db.query(false, PodcastDB.ITEMS_TABLE, new String[] {"guid", "channel_id", "title", "enclosureURL", "size"},
		                "guid='"+currentItem+"'", null, null, null, null, null);
		    	
		    	if (c.moveToFirst())
		    	{
		    		item = new Item(c);
		    	}
		    	
		    	c.close();
				
		    	if (item == null) {
		    		continue;
		    	}
				
				Log.d("DownloadTask", "run: "+item.guid);
    			
	    		String filename = Util.getPodcastFilename(item.guid);
	        	File localFile = new File(filename);
	        	
	        	if (localFile.exists() && localFile.length() == item.size) {
	        		Log.d("DownloadTask", "Already downloaded "+item.title);
	        		continue;
	        	}
	        	
	        	
	        	// Update the notification for this item
	        	notification.contentView.setTextViewText(R.id.notification_title, "Downloading "+PodcastDB.getChannelTitle(getApplicationContext(), item.channel_id));
	        	notification.contentView.setTextViewText(R.id.notification_item, item.title);
		        notification.contentView.setProgressBar(R.id.notification_progressbar, 100, 0, false);
		        mNotificationManager.notify(NOTIFICATION_ID, notification);
	        	
		        
	        	File tmpFile = new File(filename+".tmp");
	        	
	        	HttpURLConnection conn = null;
        		BufferedInputStream in = null;
        		FileOutputStream out = null;
        		
	        	try
	            {
	                URL channelURL = new URL(item.enclosureURL);
	                conn = (HttpURLConnection) channelURL.openConnection();
	                conn.setRequestProperty("Range", "bytes="+(tmpFile.length())+"-");
	                conn.setConnectTimeout(5000);
	                conn.setReadTimeout(5000);
	                conn.connect();
	                
	                in = new BufferedInputStream(conn.getInputStream(), BUFFER_SIZE);
	                out = new FileOutputStream(tmpFile, true);
	                bytes_read = (int) tmpFile.length();
	                length = Math.max(conn.getContentLength(), bytes_read);
	                progress = 0;
	                last_progress = 0;
	                
	                while (!abort && (r = in.read(data, 0, BUFFER_SIZE)) > -1)
	                {
	                	out.write(data, 0, r);
	                    bytes_read += r;
	                    
	                    // publish progress
	                    progress = (int) (bytes_read/length * 100.0f);
	                    
	                    if (progress > (last_progress + 1))
	                    {
	                    	notification.contentView.setProgressBar(R.id.notification_progressbar, 100, progress, false);
	            	        mNotificationManager.notify(NOTIFICATION_ID, notification);
	                    	last_progress = progress;
	                    }
	                }
	        		
	                
	                if (!abort) {
	                	
	                	ContentValues values = new ContentValues();
	                	values.put("size", tmpFile.length());
	                	db.update(PodcastDB.ITEMS_TABLE, values, "guid='"+item.guid+"'", null);
	                	
	                	tmpFile.renameTo(localFile);
	                }
	            }
	            catch (SocketTimeoutException e) {
	        		Log.e("DownloadTask", "Download of \""+item.title+"\" timed out");
	        	}
	            catch (IOException e) {
	                e.printStackTrace();
	            }
	        	finally {
	        		conn.disconnect();
	        		try {
						
	        			if (in != null) {
	        				in.close();
	        			}
	        			
	        			if (out != null) {
	        				out.close();
	        			}
					} catch (IOException e) {
						e.printStackTrace();
					}
	        	}
	        	
	        	notification.contentView.setProgressBar(R.id.notification_progressbar, 100, 100, false);
		        mNotificationManager.notify(NOTIFICATION_ID, notification);
		        
	        	Intent finishedIntent = new Intent(DownloadService.FINISHED_DOWNLOAD);
	        	finishedIntent.putExtra("guid", item.guid);
	        	sendBroadcast(finishedIntent);
			}
			
			this.status = Status.FINISHED;

			helper.close();

			currentItem = null;
			
			// clean up: cancel the notificatin for this Item and
			// mark objects for garbage collection
            mNotificationManager.cancel(NOTIFICATION_ID);
            data = null;
            currentItem = null;
            mNotificationManager = null;
            notification = null;
            queue.clear();
		}
	}

}
